package top.frsf.rpc.autoconfigure.proxy;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import top.frsf.rpc.annotations.RpcReference;
import top.frsf.rpc.registry.ServiceRegistry;
import top.frsf.rpc.session.ClientSessionManager;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * RPC Reference Bean 后置处理器
 *
 * @author He Kuntao
 * @since 2025/7/8 10:08
 */
@RequiredArgsConstructor
public class RpcReferenceBeanPostProcessor implements BeanPostProcessor {

    private final Map<Class<?>, Object> proxyCache = new ConcurrentHashMap<>();

    private final ServiceRegistry serviceRegistry;

    private final ClientSessionManager sessionManager;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz = bean.getClass();
        // 扫描所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            RpcReference rpcReference = field.getAnnotation(RpcReference.class);
            if (rpcReference != null) {
                // 创建或者获取代理实例
                Object proxy = proxyCache.computeIfAbsent(
                        field.getType(),
                        interfaceClass -> createProxy(interfaceClass, rpcReference)
                );
                // 设置字段可见性
                field.setAccessible(true);
                try {
                    // 设置字段值
                    field.set(bean, proxy);
                } catch (IllegalAccessException e) {
                    throw new BeanCreationException("Failed to inject RPC proxy", e);
                }
            }
        }
        return bean;
    }

    /**
     * 创建代理实例
     *
     * @param interfaceClass 接口类
     * @param rpcReference   RPC Reference 注解
     * @return 代理实例
     */
    private Object createProxy(Class<?> interfaceClass, RpcReference rpcReference) {
        return Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class[]{interfaceClass},
                new RpcInvocationHandler(interfaceClass, rpcReference, serviceRegistry, sessionManager)
        );
    }
}
